﻿using System;

namespace InfiniTec.Threading
{
	internal class ResourceReservationCollection : Collection.Generic.KeyedCollection<IResourceConstraint, ResourceReservation>
	{
		private readonly SpinWaitLock _InstanceLock = new SpinWaitLock();

		public ResourceReservationCollection() : base(reservation => reservation.Constraint)
		{
		}

		public void RegisterOperation(AsyncOperation operation)
		{
			if (operation == null) throw new ArgumentNullException("operation");

			if (operation.ResourceConstraint == null || operation.ResourceConstraint is UnrestrictedConstraint)
			{
				AsyncOperation.StartAsync(operation);
				return;
			}

			try
			{
			    _InstanceLock.Enter();
				IResourceConstraint key = operation.ResourceConstraint;
				ResourceReservation reservation;
				if (!Contains(key))
				{
					reservation = new ResourceReservation(key);
					Add(reservation);
				}
				else
				{
					reservation = this[key];
				}
				reservation.RegisterOperation(operation);
			}
            finally
			{
			    _InstanceLock.Exit();
			}
		}

		public void ReleaseOperation(AsyncOperation operation)
		{
			if (operation == null) throw new ArgumentNullException("operation");

			if (operation.ResourceConstraint == null || operation.ResourceConstraint is UnrestrictedConstraint)
			{
				return;
			}

			try
			{
                _InstanceLock.Enter();
				IResourceConstraint key = operation.ResourceConstraint;
				if (!Contains(key))
				{
					throw new InvalidOperationException(string.Format("The resource constraint {0} is not registered", key.GetType()));
				}
				ResourceReservation reservation = this[key];
				reservation.ReleaseOperation(operation);
				if (reservation.Count == 0)
				{
					Remove(key);
				}
			}
            finally
			{
			    _InstanceLock.Exit();
			}
		}
	}
}